文章目录
  1. 1. Android Studio 调试的优点
  2. 2. Android Studio 环境搭建
  3. 3. 配置 Gradle 工程
    1. 3.1. settings.gradle
    2. 3.2. build.gradle
  4. 4. 导入 Android Studio 进行调试
  5. 5. 后续改进
    1. 5.1. 函数局部变量值无法查看
    2. 5.2. 配置 gradle 工程 gitignore

Android Studio 调试的优点

Android Studio 作为 google android 官方开发工具,调试的优点简单来说有:

  1. 可以在源码打断点单步调试
  2. 打断点后可以查看各种变量的函数值,而且还支持动态修改
  3. 打断点后可以查看函数调用堆栈
  4. 打断点后可以查看进程线程信息

总之比单纯打 logcat 打印方便很多。

网上有很多使用 Android Studio 调试 android framework 的例子。原理就是把 framework 代码导入 Android Studio,然后通过挂载调试进程来先实现单步断点调试。但是网上都没说清楚如何把 framework 源码导入 Android Studio,这也是最关键的步骤。本文提供一个指导方法让大家能够顺利使用 Android Studio 调试 android framework。

Android Studio 环境搭建

这个参照我这篇文章:离线 gradle 工程模板。目前使用的是 Android Studio 4.0.1 + Gradle 5.1.1。

配置 Gradle 工程

这里我们导入的 gradle 是不需要编译的(也没法编译),所以不是正常的 gradle,需要在我们上面的 app_template 上进行一些改动。我们以导入 framework 中最常用的 system_services 为例,来说明下 gradle 工程如何配置:

gradle 工程文件树结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 大体结构和 app_template 一致,这里只说不一样的地方:
| framework_base_project/
| |----- local.properties // 本地 sdk path 配置,记得改成自己本机的路径
| |----- settings.gradle
| |----- build.gradle
| |----- gradlew
| |----- gradlew.bat
| |----- gradle/
| |---- wrapper/
| |---- gradle-wrapper.jar
| |---- gradle-wrapper.properties
|
| core/
| |---- build.gradle // framework/core 工程模块 build 脚本
|
| services/
| |---- core/
| |---- build.gradle // services 工程模块 build 脚本
|

配置 gradle 工程有2个选择:

(1). 在编译服务器配置 gradle 工程:
    好处:可以直接使用 Android Studio 编辑代码,不用来回 copy 代码
    坏处:提交的时候要注意不要把 gradle 工程文件提交上去了

(2). 在本地配置 gradle 工程:
    好处:不用担心 gradle 工程文件影响代码提交
    坏处:得从服务器 copy framework 代码到本地,如何代码有变动需要重复 copy 同步代码

如果选择 (2),就需要把 framework/base 下面的代码 copy 到本地((1)就不需要)。这里说个小技巧,copy 的时候先把在服务器建一个目录,然后用 shell cp 命令把 framework/base copy 过去,然后再去把里面一些不需要文件夹删掉(例如 tests 之类的,基本只要保留 java 的就行了),然后再 copy 到本地 window,这样会快很多。

准备好 framwork/base 源码后,按照上面工程的目录结构把工程文件 copy 到 framework/base 的源码对应目录下面,弄好的 gradle 工程目录应该是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| framework/base/
| |----- local.properties
| |----- settings.gradle
| |----- build.gradle
| |----- gradlew
| |----- gradlew.bat
| |----- gradle/
| |---- wrapper/
| |---- gradle-wrapper.jar
| |---- gradle-wrapper.properties
| core/
| |---- java/
| |---- jni/
| |---- build.gradle
|
| services/
| |---- accessibility/
| |---- appprediction/
| |---- ... ...
| |---- core/
| |---- java/
| |---- jni/
| |---- xsd/
| |---- Android.bp
| |---- build.gradle
| |---- Android.bp
| |---- art-profile
|

下面介绍下 gradle 文件怎么写:

settings.gradle

这里 settings.gradle 的配置有点小技巧,因为之前的 module 文件夹都只有一个层级的,所以 settings.gradle 直接写 module 的文件夹名字就行了。但是这些 framework/base 的不同 module 是分别在不同层级的文件夹目录下的,所以为了适应 framework/base 的源码目录结构得这么写:

1
include ':core', 'services:core'

然后在 framework/base/core 和 framework/base/services/core 下放置 build.gradle ,相当于是建立了2个 module。

build.gradle

由于我们需要导入源码,不要编译,所以 build.gradle 的脚本可以比较简单:root project 的 build.gradle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 和 app_template 相比不需要任何第三方库的引用,只需要把一些基本信息定义就行了:
ext {
buildToolsVersion = '28.0.3'
compileSdkVersion = 28
minSdkVersion = 19
targetSdkVersion = 28
versionCode = 1
versionName = '1.0.0'
}
buildscript {
repositories {
// force use offline local maven repo
//jcenter()
// 1. first search aw local repo
// 2. and then search local repo
maven() {url 'file://\\192.168.200.28\\swc_apd\\aw_maven_repo\\repository'}
mavenLocal()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
//jcenter()
maven() {url 'file://\\192.168.200.28\\swc_apd\\aw_maven_repo\\repository'}
mavenLocal()
}
}

然后是 module 的 build.gradle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 插件引入 jar 库插件
apply plugin: 'com.android.library'
android {
buildToolsVersion rootProject.ext.buildToolsVersion
compileSdkVersion rootProject.ext.compileSdkVersion
lintOptions {
abortOnError false
}
// 这里不同的 module,把包名改一下,例如这里是 framework/base/core
defaultConfig {
applicationId "com.android.framework_core"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
}
// 源码路径需要手动指定,这里把只需要指定 java 代码和 AndroidManifest 就行了。
// 实际上没 AndroidManifest.xml 文件也是没关系的。
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['java']
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}

导入 Android Studio 进行调试

上面 gradle 工程准备好之后,打开 Android Studio,选 Import project (Gradle, Eclipse ADT, etc.) 导入:
导入 Android Stuido

然后等 Android Studio 完成导入工作,点最下面的 Build 标签会看到 CONFIGURE SUCCESSFUL 的信息就表示工程成功导入了:
成功导入

注意有些时候可能会报一些编译上的错误,只要显示 CONFIGURE SUCCESSFUL 就可以了,不用理会其他错误的,例如像下面这样也是没关系的:
成功导入1

然后 adb 连上板子就可以开始调试了(注意:板子的固件必须是 userdebug 或是 eng 的,user 无法挂载调试):
开始调试

挂载上之后,可以就可以在源码上打断点了(注意你设备上跑的固件必须要是你导入的源码编译出来的,不然断点的行号会对不上),例如随便在一个休眠的函数打上断点,然后按 power 键操作设备休眠,断点就能让程序停下来:
断点调试

之后就能单步执行和查看变量信息了。这些都是 Android Studio 的操作,不会的百度一下就行了,这些不介绍这些了。

后续改进

目前来说有2点不足需要改进:

函数局部变量值无法查看

Watches 那里不显示,手动添加会显示: Cannot find local variable ‘xx’ (如上图显示)。大部分函数的局部变量有这个问题,少数的可以显示;类的成员变量可以显示。

我之前调试用的是 userdebug 的,改成 eng 的就可以查看本地变量了。应该是某个编译选项导致的。网上查了一下,apk 的说是把混淆关了就可以了。然而 services.jar 本来就没开启混淆。我尝试了下面方法仍然无法解决:

  1. 在 services 的 Android.bp 文件加: optimize: {enabled: false,}, 这个是关闭混淆的,然后 “services.jar” 就没配置混淆配置文件,应该是本来就没混淆的。
  2. 方案的 BoardConfig.mk 把 WITH_DEXPREOPT := false ,关闭 dexopt 预编译优化。

有空可以研究下是哪个编译选项导致的,这样在 userdebug 的时候把这个选项改改就行了。

配置 gradle 工程 gitignore

直接在服务器配置 gradle 工程是比把代码 copy 到本地方便的,但是 gradle 的工程文件会影响代码提交。目前只能在提交的时候自己注意一下。后续可以考虑把 framework 仓库的 gitignore 配置一下,把 gradle 的工程文件排查一下。如果文件标准,都可以把 gradle 文件也上传了。

文章目录
  1. 1. Android Studio 调试的优点
  2. 2. Android Studio 环境搭建
  3. 3. 配置 Gradle 工程
    1. 3.1. settings.gradle
    2. 3.2. build.gradle
  4. 4. 导入 Android Studio 进行调试
  5. 5. 后续改进
    1. 5.1. 函数局部变量值无法查看
    2. 5.2. 配置 gradle 工程 gitignore